package com.fsoft.core.common.base.contorller;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import com.alibaba.fastjson.JSONObject;
import com.fsoft.core.Global;
import com.fsoft.core.common.base.entity.UploadFileVo;
import com.fsoft.core.common.base.entity.UserVo;
import com.fsoft.core.context.FSContext;
import com.fsoft.core.utils.FileManager;
import com.fsoft.core.utils.HttpContextUtils;
import com.fsoft.core.utils.OgnlUtils;
import com.fsoft.core.utils.RetVo;
import com.fsoft.core.utils.WebUtils;

/**
 * F-Soft 基础Controller
 * @package com.fsoft.core.common.base
 * @author Fish
 * @email it.fish2010@foxmail.com
 * @date 2019-11-02
 * @CopyRight © F-Soft
 **/
public abstract class BaseController {

	private static final Logger logger = LoggerFactory.getLogger(BaseController.class);

	/***
	 * 文件上传： 按单位ORGID标识分目录保存
	 * @author licheng
	 * @create 2020-12-01
	 * @param params
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	protected List<UploadFileVo> commonsUploadEx(@RequestParam Map<String, Object> params, HttpServletRequest request, HttpServletResponse response) throws Exception {
		String rootDir = "workSpace";
		String rootDirCatalog = rootDir;
		if (OgnlUtils.isNotEmpty(params.get(Global.F_ORGID)))
			rootDirCatalog = rootDir + FileManager.SEPARATOR + params.get(Global.F_ORGID).toString();
		List<UploadFileVo> ret = commonsUploadEx(rootDirCatalog, request, response);
		return ret;
	}

	/***
	 * 上传文件通用函数,支持多文件上传
	 * @author licheng
	 * @date 2021-04-11
	 * @param spaceName 上传文件的根目录名称
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	protected List<UploadFileVo> commonsUploadEx(String spaceName, HttpServletRequest request, HttpServletResponse response) throws Exception {
		List<UploadFileVo> ret = new ArrayList<UploadFileVo>();
		// 创建多部件解析器
		CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
		// 检测是否有文件上传
		if (multipartResolver.isMultipart(request)) {
			// 将 HttpServletRequest 造型为 MultipartHttpServletRequest
			MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
			// 获取请求中所有的文件名称
			Iterator<String> iterator = multiRequest.getFileNames();
			while (iterator.hasNext()) {
				// 获取上传的文件
				MultipartFile multiFile = multiRequest.getFile(iterator.next());
				if (multiFile != null) {
					// 文件扩展名
					String extension = FileManager.getFileNameExt(multiFile.getOriginalFilename());
					// 判断传入的是文件名是否为空,如果是空就用原来的名字 modify by gk 原因不是每个上传都需要新的 避免文件数量过多，取32位id的前两位作为文件夹
					String newFName = OgnlUtils.randomLowerCaseId() + "." + extension;
					// 绝对路径
					String absolutePath = FileManager.getSysoptRealPath(FileManager.SEPARATOR + spaceName + FileManager.SEPARATOR + newFName);
					File file = FileManager.createFile(absolutePath);
					multiFile.transferTo(file);
					// 相对路径
					String relativePath = FileManager.getWebPath(spaceName + FileManager.SEPARATOR + newFName);
					ret.add(new UploadFileVo(multiFile.getOriginalFilename(), newFName, relativePath));
				}
			}
		}
		return ret;
	}

	/** 文件下载完标识，设置保存在cookie中的过期时间，单位：秒 */
	protected static final int DOWNLOAD_ID_COOKIE_EXPIRY = 5;
	/** 文件下载id参数名 */
	protected static final String DOWNLOAD_ID_NAME = "_downloadid";

	/**
	 * 文件下载通用方法,支持大文件下载
	 * @author chenshengyu @date 2019年1月25日 上午9:38:59
	 * @param request
	 * @param response
	 * @param fullFilePath 需要下载的文件的全路径（绝对路径）
	 * @return 文件不存在时返回相应的说明，前端可以根据说明做相应的提示
	 * @throws Exception
	 */
	protected void commonsDownloadEx(HttpServletRequest request, HttpServletResponse response, String fullFilePath) throws Exception {
		boolean isIE = WebUtils.isIEBrowser(request);
		setResponseContentTypeAndStatus(response, (isIE ? MediaType.TEXT_PLAIN : MediaType.APPLICATION_JSON_UTF8), HttpStatus.OK);
		InputStream is = null;
		RetVo retVo = new RetVo();
		// 文件下载完成标志
		String downloadId = request.getParameter(DOWNLOAD_ID_NAME);
		try {
			if (StringUtils.isBlank(fullFilePath)) {
				logger.error("无效文件路径：path = {}", fullFilePath);
				retVo.setMsg("下载失败：无效文件路径 path = " + fullFilePath);
				writeJsonToResponse(response, retVo);
				return;
			}
			// 格式化文件路径
			String path = FileManager.formatWebPath(fullFilePath);
			// 提取文件名
			String fileName = FileManager.extractFileNameWithExt(path);
			File file = new File(path);
			if (!file.exists()) {
				logger.error("文件不存在：path = {}", fullFilePath);
				retVo.setMsg("下载失败：文件" + fileName + "不存在");
				writeJsonToResponse(response, retVo);
				return;
			} else if (!file.isFile()) {
				logger.error("无效文件：path = {}", fullFilePath);
				retVo.setMsg("下载失败：无效文件" + fileName);
				writeJsonToResponse(response, retVo);
				return;
			}

			is = new FileInputStream(file);
			// 处理文件名中包含中文的情况
			String downloadFileName = new String(fileName.getBytes("UTF-8"), "iso-8859-1");
			// 返回文件流
			response.setHeader("Content-Disposition", "attachment; filename=" + downloadFileName);
			setResponseContentTypeAndStatus(response, MediaType.APPLICATION_OCTET_STREAM, (isIE ? HttpStatus.OK : HttpStatus.CREATED));
			if (StringUtils.isNotBlank(downloadId)) {
				String cookieName = DOWNLOAD_ID_NAME + downloadId;
				HttpContextUtils.setCookie(cookieName, downloadId, DOWNLOAD_ID_COOKIE_EXPIRY, request, response);
			}
			IOUtils.copyLarge(is, response.getOutputStream());
			is.close();
			is = null;
			response.flushBuffer();
		} catch (Exception e) {
			logger.error("文件下载发生异常,path = {}", fullFilePath, e);
			retVo.setMsg("文件下载异常：" + e.getMessage());
			setResponseContentTypeAndStatus(response, (isIE ? MediaType.TEXT_PLAIN : MediaType.APPLICATION_JSON_UTF8), HttpStatus.OK);
			writeJsonToResponse(response, retVo);
		} finally {
			if (null != is) {
				is.close();
			}
		}
	}

	/***
	 * F-Soft 获取当前登录用户信息
	 * @author Fish(it.fish2010@foxmail.com)
	 * @date 2019-11-02
	 * @return
	 */
	protected final UserVo getUser() {
		return FSContext.getInstance().getUser();
	}

	/***
	 * F-Soft 当前登录用户的ID
	 * @author Fish(it.fish2010@foxmail.com)
	 * @date 2019-11-02
	 * @return
	 */
	protected final String getUserId() {
		return getUser().getId();
	}

	/***
	 * F-Soft 当前登录用户的登录帐号
	 * @author Fish(it.fish2010@foxmail.com)
	 * @date 2019-11-02
	 * @return
	 */
	protected final String getLoginAcct() {
		return getUser().getLoginAcct();
	}

	/**
	 * 将retVo转成json后写入到response中
	 * @author chenshengyu @date 2019年1月25日 下午3:46:17
	 * @param response
	 * @param retVo
	 * @throws Exception
	 */
	protected void writeJsonToResponse(HttpServletResponse response, RetVo retVo) throws Exception {
		if (null != retVo) {
			byte[] bytes = JSONObject.toJSONString(retVo).getBytes("UTF-8");
			OutputStream out = response.getOutputStream();
			out.write(bytes, 0, bytes.length);
			out.flush();
			out.close();
		}
	}

	/**
	 * 设置response的ContentType和HttpStatus
	 * @author chenshengyu @date 2019年1月25日 下午3:56:21
	 * @param mediaType
	 * @param status
	 */
	protected void setResponseContentTypeAndStatus(HttpServletResponse response, MediaType mediaType, HttpStatus status) {
		setResponseContentType(response, mediaType);
		setResponseHttpStatus(response, status);
	}

	/**
	 * 设置response的ContentType
	 * @author chenshengyu @date 2019年1月25日 下午4:01:15
	 * @param response
	 * @param mediaType
	 */
	protected void setResponseContentType(HttpServletResponse response, MediaType mediaType) {
		if (null != mediaType) {
			response.setContentType(mediaType.toString());
		}
	}

	/**
	 * 设置response的HttpStatus
	 * @author chenshengyu @date 2019年1月25日 下午4:01:22
	 * @param response
	 * @param status
	 */
	protected void setResponseHttpStatus(HttpServletResponse response, HttpStatus status) {
		if (null != status) {
			response.setStatus(status.value());
		}
	}

}
